package cz.cvut.fel.gfupan.gui.tab;

import cz.cvut.fel.gfupan.gui.action.InputKeyListener;
import cz.cvut.fel.gfupan.gui.action.TextFocusListener;
import cz.cvut.fel.gfupan.gui.component.Observable;
import cz.cvut.fel.gfupan.gui.component.Observer;
import cz.cvut.fel.gfupan.gui.component.TextComponent;
import cz.cvut.fel.gfupan.gui.execution.AutoFill;
import cz.cvut.fel.gfupan.gui.execution.Executable;
import cz.cvut.fel.gfupan.gui.util.Coordinates;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import javax.swing.JComponent;
import javax.swing.JPanel;
import javax.swing.text.JTextComponent;

public abstract class AbstractTab extends JPanel implements Tabbable {

    protected Map<Coordinates, JComponent> tab = new HashMap<Coordinates, JComponent>();
    protected Coordinates actualCoords;
    protected List<Integer> maxColumn = new ArrayList<Integer>();

    @Override
    public void moveFocusByRow(int row) {
        moveFocusByCoords(row, 0);
    }

    @Override
    public void moveFocusByCoords(int row, int id) {
        if (row < 0 || row >= maxColumn.size() || id >= maxColumn.get(row)) {
            return;
        }

        JComponent item = tab.get(new Coordinates(row, id));
        if (item != null) {
            actualCoords = new Coordinates(row, id);
            item.requestFocus();
        } else {//try to use component in left - 1 when there is actually nothing
            moveFocusByCoords(row, --id);
        }
    }

    @Override
    public void moveFocusUp() {
        int row = ((actualCoords.row - 1) + maxColumn.size()) % maxColumn.size();
        int col = (actualCoords.row - 1 < maxColumn.size()
                && actualCoords.row - 1 >= 0 ? actualCoords.column % maxColumn.get(actualCoords.row - 1) : 0);
        moveFocusByCoords(row, col);
    }

    @Override
    public void moveFocusDown() {
        int row = ((actualCoords.row + 1) + maxColumn.size()) % maxColumn.size();
        int col = (actualCoords.row + 1 < maxColumn.size() ? actualCoords.column % maxColumn.get(actualCoords.row + 1) : 0);
        moveFocusByCoords(row, col);
    }

    @Override
    public void moveFocusLeft() {
        int row = actualCoords.row;
        int col = ((actualCoords.column - 1) + maxColumn.get(row)) % maxColumn.get(row);
        if (actualCoords.column - 1 < 0 && actualCoords.row - 1 >= 0) {
            row = actualCoords.row - 1;
            col = maxColumn.get(actualCoords.row - 1) - 1;
        } else if (actualCoords.column - 1 < 0 && actualCoords.row - 1 < 0) {
            row = maxColumn.size() - 1;
            col = 0;
        }
        moveFocusByCoords(row, col);
    }

    @Override
    public void moveFocusRight() {
        int row = actualCoords.row;
        int col = ((actualCoords.column + 1) + maxColumn.get(row)) % maxColumn.get(row);
        if (actualCoords.column + 1 >= maxColumn.get(actualCoords.row) && actualCoords.row + 1 <= maxColumn.size()) {
            row = actualCoords.row + 1;
            col = 0;
        }
        moveFocusByCoords(row, col);
    }

    protected final void fillTab(int id, JComponent... f) {
        if (f != null && f.length > 0) {
            int counter = 0;
            for (JComponent comp : f) {
                tab.put(new Coordinates(id, counter++), comp);
            }
            maxColumn.add(id, counter);
        }
    }

    @Override
    public void setActionListeners() {
    }

    @Override
    public void setFocusListeners() {
    }

    @Override
    public void setKeyListeners() {
    }

    @Override
    public void setObservers() {
    }

    public void setActualPosition(JTextComponent pos) {
        for (Entry<Coordinates, JComponent> entry : tab.entrySet()) {
            if (pos.equals(entry.getValue())) {
                actualCoords = entry.getKey();
            }
        }
    }

    protected final void setFocusListener(JTextComponent compo) {
        compo.addFocusListener(new TextFocusListener(compo));
    }

    protected final void setKeyListener(JComponent compo) {
        compo.addKeyListener(new InputKeyListener(this));
    }

    protected final void clearField(JTextComponent text) {
        if (text instanceof TextComponent) {
            ((TextComponent) text).clear();
        } else {
            text.setText("");
        }
    }

    protected final void addObserver(JTextComponent owning, JTextComponent observer) {
        //ugly :-(
        if (owning instanceof Observable && observer instanceof Observer) {
            ((Observable) owning).addObserver((Observer) observer);
        }
    }

    protected final void setExecutor(JTextComponent observer, Executable executer) {
        if (observer instanceof Observer) {
            ((Observer) observer).setExecutor(executer);
        }
    }

    protected final void autoFill(JTextComponent owning, JTextComponent observer) {
        addObserver(owning, observer);
        setExecutor(observer, new AutoFill(observer, owning));
    }

    protected final void autoFillLoop(JTextComponent... component) {
        if (component != null && component.length > 1) {
            for (int i = 0; i < component.length - 1; i++) {
                autoFill(component[i], component[i + 1]);
            }
        }
    }
}
